<?php

    /**
     * Abstract
     *
     * Class which represents the abstract part of update system
     *
     * @package      Core
     * @subpackage   Update
     *
     * @license      GNU Lesser General Public Licence see LICENCE-LGPL file or http://www.gnu.org/licenses/lgpl.html
     */
    abstract class Core_Updater_Abstract extends Doctrine_Migration 
    {
    ////////////////////////////////////////////////////////////////////////////
    //                                Variables                               //
    ////////////////////////////////////////////////////////////////////////////
    
        const TYPE_SERUM        = 0;
        const TYPE_PROJECT      = 1;
        const VERSION_SERUM     = 'version_serum';
        const VERSION_PROJECT   = 'version_project';
        const LOG_SERUM         = 'update_serum.log';
        const LOG_PROJECT       = 'update_project.log';
        
        protected $_pathes      = array();
        protected $_migrationsPath;
        protected $_backupsPath;
        protected $_logPath;
        protected $_currentType;
        protected $_authorizedAccounts = array('admin');
    
    ////////////////////////////////////////////////////////////////////////////
    //                            Getters / Setters                           //
    ////////////////////////////////////////////////////////////////////////////
                
        /**
         * Constructor
         * 
         * @param       integer     $type: Type of migration
         */
        public function __construct($type)
        {
        	if ($type == self::TYPE_SERUM) {
        		$this->_pathes['migrations']  = SERUM_UPDATE_PATH;
                $this->_pathes['backups']     = Core_IO_Path::combine(PROJECT_VAR_PATH, 'backups', 'serum');
                $this->_pathes['log']         = Core_IO_Path::combine(PROJECT_VAR_PATH, 'log', self::LOG_SERUM);
        	} else if ($type == self::TYPE_PROJECT) {
                $this->_pathes['migrations']  = PROJECT_UPDATE_PATH;
                $this->_pathes['backups']     = Core_IO_Path::combine(PROJECT_VAR_PATH, 'backups', 'project');
                $this->_pathes['log']         = Core_IO_Path::combine(PROJECT_VAR_PATH, 'log', self::LOG_PROJECT);
        	} else {
        		throw new Core_Exception('Type of migration not defined', 0);
        	}
        	
            if (!Core_IO_Directory::exists($this->_pathes['backups']))
                Core_IO_Directory::createDirectory($this->_pathes['backups']);
                
            $this->_currentType         = $type;
        	parent::__construct($this->_pathes['migrations']);
        }
        
        private function isSerum()
        {
        	return $this->_currentType == self::TYPE_SERUM ? true : false;
        }
        private function isProject()
        {
            return $this->_currentType == self::TYPE_PROJECT ? true : false;
        }
        
		/**
		 * createMigrationTable
		 * 
		 * Creates the migration table used to store the current version
		 *
		 * @return void
		 */
		protected function _createMigrationTable()
		{
		    $conn = Doctrine_Manager::connection();
		    
		    try {
		        $conn->export->createTable($this->_migrationTableName, array(self::VERSION_SERUM    => array('type' => 'integer', 'size' => 11, 'default' => 0),
		                                                                     self::VERSION_PROJECT  => array('type' => 'integer', 'size' => 11, 'default' => 0)));
		        
		        return true;
		    } catch(Exception $e) {
		        return false;
		    }
		}
        
	    /**
	     * setCurrentVersion
	     *
	     * Sets the current version in the migration table
	     *
	     * @param string $number 
	     * @return void
	     */
        public function setCurrentVersion($number)
        {
            if ($this->isSerum()) {
                $return = $this->_setCurrentVersion(self::VERSION_SERUM, $number);
            } elseif ($this->isProject()) {
                $return = $this->_setCurrentVersion(self::VERSION_PROJECT, $number);
            }
            
            return $return;
        }
	    private function _setCurrentVersion($version_type, $number)
	    {
	        $conn = Doctrine_Manager::connection();
	        
	        if ($this->hasMigrated()) {
	            $conn->exec("UPDATE " . $this->_migrationTableName . " SET ".$version_type." = $number");
	        } else {
	            $conn->exec("INSERT INTO " . $this->_migrationTableName . " (".$version_type.") VALUES ($number)");
	        }
	    }
	
	    /**
	     * getCurrentVersion
	     *
	     * Get the current version of the database
	     *
	     * @return void
	     */
	    public function getCurrentVersion()
	    {
            if ($this->isSerum()) {
                $return = $this->_getCurrentVersion(self::VERSION_SERUM);
            } elseif ($this->isProject()) {
                $return = $this->_getCurrentVersion(self::VERSION_PROJECT);
            }
            
            return $return;
	    }
	    private function _getCurrentVersion($version_type)
	    {
	        $conn = Doctrine_Manager::connection();
	        
	        $result = $conn->fetchColumn("SELECT ".$version_type." FROM " . $this->_migrationTableName);
	        
	        return isset($result[0]) ? $result[0]:0;
	    }
	
	    /**
	     * hasMigrated
	     *
	     * Returns true/false for whether or not this database has been migrated in the past
	     *
	     * @return void
	     */
	    public function hasMigrated()
	    {
	    	if ($this->isSerum()) {
	    		$return = $this->_hasMigrated(self::VERSION_SERUM);
	    	} elseif ($this->isProject()) {
                $return = $this->_hasMigrated(self::VERSION_PROJECT);
	    	}
	    	
	    	return $return;
	    }
	    private function _hasMigrated($version_type)
	    {
	        $conn = Doctrine_Manager::connection();
	        
	        $result = $conn->fetchColumn("SELECT ".$version_type." FROM " . $this->_migrationTableName);
	        
	        return isset($result[0]) ? true:false; 
	    }
	    
	    public function migrate($to = null, $dryRun = false)
	    {
            $namespace  = Core_Authentification::getNamespace();
            $date       = new Zend_Date();
            
	    	$values               = array();
	    	$values['time']       = $date->get(Zend_Date::W3C);
	    	$values['to']         = $to === null ? $this->getLatestVersion() : $to;
	    	$values['from']       = $this->getCurrentVersion();
	    	$values['direction']  = $values['from'] > $values['to'] ? 'downgraded' : 'upgraded';
	    	$values['who']        = $namespace->firstname.' '.$namespace->name.' (id: '.$namespace->id.')';
	    	
            $contentLine = '('.$values['time'].') - '.$values['who'].' has '.$values['direction'].' from v.'.$values['from'].' to v.'.$values['to'];
            
            $fs = Core_IO_File::appendText($this->_pathes['log']);
            
	        if ($fs instanceof Core_IO_FileStream) {
                $fs->write($contentLine."\r\n");
                $fs->close();
            }
            
	    	parent::migrate($to, $dryRun);
	    }
	    
	    public function getAuthorizedAccounts()
	    {
	    	return $this->_authorizedAccounts;
	    }
	    
	    public function getPath($path)
	    {
	    	return $this->_pathes[$path];
	    }
    }
